use crate::dbs::sqlite::Peer;
use async_graphql::{Context, ID};
use async_graphql::{Error, ErrorExtensions};
use chrono::prelude::*;
use rbatis::rbatis::Rbatis;

use base64::{decode, encode};

use crate::features::explorer::{
  self,
  modelsgqo::{Editor, EditorFilterIn, File, FileFilterIn, Folder, FolderFilterIn},
};
use crate::features::gnode::{self, Node};
use crate::features::quizzes::{
  self,
  modelsgqo::QuizItem,
  modelsgqo::{Quiz, QuizFilterIn},
};
use crate::features::tasks::{
  self,
  modelsgqo::{Task, TaskFilterIn},
};
use crate::features::users::{self, modelsgqo::User};
use crate::util::constant::GqlResult;

pub struct QueryRoot;

#[async_graphql::Object]
impl QueryRoot {
  /// 获取一批用户
  async fn list_users(&self, ctx: &Context<'_>) -> GqlResult<Vec<User>> {
    let rbatis_pool = ctx.data_unchecked::<Rbatis>();
    let user_dbos = users::services::all_users(rbatis_pool).await;

    if user_dbos.len() > 0 {
      let user_gqos: Vec<User> = user_dbos.into_iter().map(|x| x.into()).collect();
      Ok(user_gqos)
    } else {
      Err(Error::new("1-all-users").extend_with(|_, e| e.set("details", "No records")))
    }
  }

  /// 获取一批试卷Quiz
  async fn list_quizzes(
    &self,
    ctx: &Context<'_>,
    filter: Option<QuizFilterIn>,
    page: Option<usize>,
  ) -> GqlResult<Vec<Quiz>> {
    let rbatis_pool = ctx.data_unchecked::<Rbatis>();
    let my_peer = ctx.data_unchecked::<Peer>();
    quizzes::services::all_quizzes(my_peer, rbatis_pool).await
  }

  /// 获取一批试题QuizItem
  async fn list_quizitems(
    &self,
    ctx: &Context<'_>,
    page: Option<usize>,
  ) -> GqlResult<Vec<QuizItem>> {
    let rbatis_pool = ctx.data_unchecked::<Rbatis>();
    quizzes::services::all_quizitems(rbatis_pool).await
  }

  /// 获取一批任务Task
  async fn list_tasks(
    &self,
    ctx: &Context<'_>,
    filter: Option<TaskFilterIn>,
    page: Option<usize>,
  ) -> GqlResult<Vec<Task>> {
    let rbatis_pool = ctx.data_unchecked::<Rbatis>();
    let my_peer = ctx.data_unchecked::<Peer>();

    // refresh tasks' difficulty & recall probability & recall time
    let refesh_rs = tasks::services::caculate_tasks_ebp(rbatis_pool, my_peer, None).await;
    println!("caculate_tasks_ebp result ={:?}", refesh_rs);

    let task_dbos = tasks::services::get_tasks_by(rbatis_pool, my_peer, filter).await;
    if task_dbos.is_ok() {
      let task_gqos: Vec<Task> = task_dbos.unwrap().into_iter().map(|x| x.into()).collect();

      Ok(task_gqos)
    } else {
      Err(Error::new("1-all-tasks").extend_with(|_, e| e.set("details", "No records")))
    }
  }

  /// 获取一批目录夹Folder
  async fn list_folders(
    &self,
    ctx: &Context<'_>,
    filter: Option<FolderFilterIn>,
    page: Option<usize>,
  ) -> GqlResult<Vec<Folder>> {
    let rbatis_pool = ctx.data_unchecked::<Rbatis>();
    let my_peer = ctx.data_unchecked::<Peer>();

    let folder_dbos = explorer::services::get_folders_by(rbatis_pool, my_peer, filter).await;
    if folder_dbos.is_ok() {
      let folder_gqos: Vec<Folder> = folder_dbos.unwrap().into_iter().map(|x| x.into()).collect();

      Ok(folder_gqos)
    } else {
      Err(Error::new("1-list-folders").extend_with(|_, e| e.set("details", "No records")))
    }
  }

  /// 获取一批文件File
  async fn list_files(
    &self,
    ctx: &Context<'_>,
    filter: Option<FileFilterIn>,
    page: Option<usize>,
  ) -> GqlResult<Vec<File>> {
    let rbatis_pool = ctx.data_unchecked::<Rbatis>();
    let my_peer = ctx.data_unchecked::<Peer>();

    let file_dbos = explorer::services::get_files_by(rbatis_pool, my_peer, filter).await;
    if file_dbos.is_ok() {
      let file_gqos: Vec<File> = file_dbos.unwrap().into_iter().map(|x| x.into()).collect();

      Ok(file_gqos)
    } else {
      Err(Error::new("1-list-files").extend_with(|_, e| e.set("details", "No records")))
    }
  }

  /// find my top folder
  async fn find_top_folder(&self, ctx: &Context<'_>) -> GqlResult<Option<Folder>> {
    let rbatis_pool = ctx.data_unchecked::<Rbatis>();
    let my_peer = ctx.data_unchecked::<Peer>();

    let root_folder = explorer::services::find_top_folder(rbatis_pool).await;
    Ok(root_folder)
  }

  /// 获取一批Editor
  async fn list_editors(
    &self,
    ctx: &Context<'_>,
    filter: Option<EditorFilterIn>,
    page: Option<usize>,
  ) -> GqlResult<Vec<Editor>> {
    let rbatis_pool = ctx.data_unchecked::<Rbatis>();
    let my_peer = ctx.data_unchecked::<Peer>();

    let editor_dbos = explorer::services::get_editors_by(rbatis_pool, my_peer, filter).await;
    if editor_dbos.is_ok() {
      let editor_gqos: Vec<Editor> = editor_dbos.unwrap().into_iter().map(|x| x.into()).collect();

      Ok(editor_gqos)
    } else {
      Err(Error::new("1-list-editors").extend_with(|_, e| e.set("details", "No records")))
    }
  }

  /// me
  async fn me(&self, ctx: &Context<'_>) -> GqlResult<User> {
    Ok(User {
      ..Default::default()
    })
  }

  /// Similar to Github: GitHub's GraphQL API allows retrieval of a node by its ID, called a "direct node lookup".
  async fn node(&self, ctx: &Context<'_>, id: ID) -> GqlResult<Node> {
    let node = gnode::node(ctx, &id.0).await;
    match node {
      Some(v) => Ok(v),
      None => Err(Error::new("2-node-by-id").extend_with(|_, e| e.set("details", "No record"))),
    }
  }
}
